package com.bree.erp.jsf.view;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.ViewScoped;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.model.SelectItem;
import javax.faces.model.SelectItemGroup;

import org.apache.log4j.Logger;
import org.primefaces.event.SelectEvent;

import com.bree.erp.entity.ItemRequest;
import com.bree.erp.entity.PartUnit;
import com.bree.erp.entity.PoItem;
import com.bree.erp.entity.Price;
import com.bree.erp.entity.PurchaseOrder;
import com.bree.erp.entity.PurchaseRequisition;
import com.bree.erp.entity.Status;
import com.bree.erp.entity.Supplier;
import com.bree.erp.jsf.datatable.ListBreeDataModel;
import com.bree.erp.jsf.httpsess.SessionDataKey;
import com.bree.erp.jsf.httpsess.UserSessionData;
import com.bree.erp.jsf.util.CurrentInstance;
import com.bree.erp.jsf.vo.ItemRequestDT;
import com.bree.erp.service.PoItemService;
import com.bree.erp.service.PriceService;
import com.bree.erp.service.PurchaseOrderService;
import com.bree.erp.service.PurchaseRequisitionService;
import com.bree.erp.service.SupplierService;

@ManagedBean(name="dtPurchaseOrderRequestView")
@ViewScoped
public class PurchaseOrderRequestView {

	private static Logger LOG = Logger.getLogger(PurchaseOrderRequestView.class);

	@ManagedProperty(value="#{userSession}")
	private UserSessionData userSession;
	
	@EJB
	private SupplierService supplierService;
	
	@EJB
	private PriceService priceService;
	
	@EJB
	private PurchaseOrderService poService;
	
	@EJB
	private PoItemService poItemService;
	
	@EJB
	private PurchaseRequisitionService prService;
	
	@EJB
	private PoItemService piService;
			
	private ListBreeDataModel<ItemRequestDT> requestDataModel;
	
	private ItemRequestDT selectedItem;

	private List<Supplier> suppliers;
	
	private Long supplierId;
	private double qtyOrder;
	private double unitPrice;
	private boolean saveSupplierPrice;
	private double maxQty;
	
	private List<SelectItem> selectItems;

	private PurchaseRequisition purchaseRequisition;
	
	@PostConstruct
	public void init() {
		
		suppliers = new ArrayList<Supplier>(supplierService.list());
		
		Long id = userSession.get(
				SessionDataKey.SELECTED_PURCHASE_REQUISITION_ID, Long.class);

		if (id != null) {
			purchaseRequisition = prService.read(id, true);
			initDataModel(purchaseRequisition);
		}
		
	}
		
	private void initDataModel(PurchaseRequisition purchaseRequisition) {
		
		
		Map<PartUnit, Double> mapQty = piService.totalQuantity(purchaseRequisition.getId());
		
		List<ItemRequestDT> requests = new ArrayList<ItemRequestDT>();
		
		int count = 0;
		for(ItemRequest item : purchaseRequisition.getRequests()) {
			
			ItemRequestDT dt = new ItemRequestDT(item);
			
			Double ordered = mapQty.get(new PartUnit(dt.getPartUnitId()));
			
			dt.setItemNumber(++count);
			dt.setOrderedQuantity(ordered);
			
			requests.add(dt);
		}
		
		requestDataModel = new ListBreeDataModel<ItemRequestDT>(requests);
	}
	
    public void onRowSelect(SelectEvent event) {

        
        selectedItem = ((ItemRequestDT) event.getObject());
        		
    	String msgStr = selectedItem.getId() + "";
        FacesMessage msg = new FacesMessage("Item Selected", msgStr);
        FacesContext.getCurrentInstance().addMessage(null, msg);

        
        LOG.debug("Selected Item Request ID: " + msgStr);
        
        // re-init
        supplierId = null;
        selectItems = null;
        
        LOG.debug("auto selecting first supplier...");
        if(selectedItem != null) {
        	List<SelectItem> list = getSuppliers();
        	LOG.debug("supplier group size: " + list.size());
        	for(SelectItem item : list) {
        		SelectItem first = getFirstItem(item);
        		if(first != null) {
        			supplierId = (Long) first.getValue();
        			LOG.debug("1st supplier in list: " + first.getLabel() + "; id = " + supplierId);
        			Long partUnitId = selectedItem.getPartUnitId();
        			Price price = priceService.read(partUnitId, supplierId);
        			if(price != null) {
        				this.unitPrice = price.getPricePerUnit();
        			} else {
        				this.unitPrice = 0;
        			}
        			break;
        		}
        	}
        }
        

    	// compute for available quantity to avoid excess amount in ordering.
    	double qtyRequested = selectedItem.getQuantity();
    	double qtyOrdered = poItemService.totalQuantity(selectedItem.getPrId(), selectedItem.getPartUnitId());
    	
    	if(qtyRequested > qtyOrdered) {
    		maxQty = qtyRequested - qtyOrdered;
    	} else {
    		maxQty = 0;
    	}
    	
    	// pre-set max quantity
    	qtyOrder = maxQty;
    }
    
    private SelectItem getFirstItem(SelectItem item) {
		
    	if(item instanceof SelectItemGroup) {
    		SelectItemGroup group = (SelectItemGroup) item;
    		SelectItem[] arr = group.getSelectItems();
    		if(arr != null && arr.length > 0) {
    			return arr[0];
    		}
    	}
    
		return item;
	}

	public List<SelectItem> getSuppliers() {

    	if(selectItems != null) {
    		// return cached
    		return selectItems;
    	}
    	selectItems = new ArrayList<SelectItem>();
    	
    	if(suppliers != null && selectedItem != null) {
    		
    		List<SelectItem> withRecordList = new ArrayList<SelectItem>();
    		List<SelectItem> withoutRecordList = new ArrayList<SelectItem>();
    		
    		for(Supplier supplier : suppliers) {
    			Long partUnitId = selectedItem.getPartUnitId();
    			Price price = priceService.read(partUnitId, supplier.getId());
    			if(price != null) {
    				withRecordList.add(new SelectItem(supplier.getId(), supplier.getName()));
    			} else {
    				withoutRecordList.add(new SelectItem(supplier.getId(), supplier.getName()));
    			}
    		}
    		
    		if(!withRecordList.isEmpty()) {
        		SelectItemGroup withRecordGroup = new SelectItemGroup("With Existing Records");
        		withRecordGroup.setSelectItems(withRecordList.toArray(new SelectItem[withRecordList.size()]));
        		
    			selectItems.add(withRecordGroup);
    		}
    		
    		if(!withoutRecordList.isEmpty()) {
        		SelectItemGroup withoutRecordGroup = new SelectItemGroup("Without Records");
        		withoutRecordGroup.setSelectItems(withoutRecordList.toArray(new SelectItem[withoutRecordList.size()]));
        		
    			selectItems.add(withoutRecordGroup);
    		}
    	}
    	
    	return selectItems;
    }
    
    public void supplierHasChanged(AjaxBehaviorEvent vce){  
    	  Long supplierId= (Long) ((UIOutput) vce.getSource()).getValue();
    	  
    	  LOG.debug("supplier changed to: " + supplierId + "; for partUnit: " + selectedItem.getPartUnitId());
    	   
    	  Price price = supplierId == null ? null : priceService.read(selectedItem.getPartUnitId(), supplierId);
    	  if(price != null) {
    		  this.unitPrice = price.getPricePerUnit();
    	  } else {
    		  this.unitPrice = 0.0;
    	  }
		  LOG.debug("Unit price updated to: " + this.unitPrice);
    }
    
    public String placeOrder() {
    	
    	LOG.debug("unit price: " + this.unitPrice + "; save: " + this.saveSupplierPrice);
    	
    	Long prId = selectedItem.getPrId();
    	
    	// add PO with an open status only.
    	PurchaseOrder po = poService.read(prId, supplierId, Status.OPEN);
    	if(po == null) {
    		// create new PO.
    		po = poService.create(prId, supplierId, userSession.getLoggedInUser().getEmail());
    	}

    	// add order item.
		Long partUnitId = selectedItem.getPartUnitId();
    	poItemService.create(po.getId(), partUnitId, this.unitPrice, qtyOrder);
    		
    	if(this.saveSupplierPrice) {
    		
    		// check for existing record
    		Price price = priceService.read(partUnitId, supplierId);
    		
    		if(price == null) {
    			// create new entry
	    		Price newPrice = priceService.create(partUnitId, supplierId, this.unitPrice);
	    		LOG.debug("New price created : " + newPrice.getId() + " [" + supplierId + ":" + partUnitId + ":" + unitPrice + "]");
    		} else {
    			// update existing entry
    			priceService.update(price.getId(), this.unitPrice);
    		}
    		
    		// invalidate cache
    		selectItems = null;
    	}
    	
    	// force reload
    	purchaseRequisition = prService.read(prId, true);
    	
    	return "/po/main";
    	
    }
    
    public List<PurchaseOrder> getPurchaseOrders() {
    
    	Long prId = this.userSession.get(SessionDataKey.SELECTED_PURCHASE_REQUISITION_ID, Long.class);
    	if(prId != null) {
    		PurchaseRequisition pr = purchaseRequisition; //cprService.read(prId, true);
    		
    		Collection<PurchaseOrder> orders = pr.getOrders();
    		if(orders != null) {
    			List<PurchaseOrder> list = new ArrayList<PurchaseOrder>();
    			
    			for(PurchaseOrder order : orders) {
    				PurchaseOrder freshOrder = poService.read(order.getId(), true);
    				
    				if(freshOrder != null && freshOrder.getItems() != null) {
    					int itemNum = 1;

    					for(PoItem item : freshOrder.getItems()) {
    						item.setItemNumber(itemNum++);
    					}
    				}
    				
    				list.add(freshOrder);
    			}
    			
    			return list;
    		}
    	}
    	
    	
    	return Collections.emptyList();
    }
    
    /**
     * index CSV for Primefaces accordion component. 
     * @return
     */
    public String getActiveIndex() {
    	StringBuilder sb = new StringBuilder();
    	
    	Long prId = this.userSession.get(SessionDataKey.SELECTED_PURCHASE_REQUISITION_ID, Long.class);
    	if(prId != null) {
    		PurchaseRequisition pr = prService.read(prId);
    		int size = pr.getOrders() == null ? 0 : pr.getOrders().size();
    		
    		for(int k = 0; k < size; k++) {
    			if(k > 0) {
    				sb.append(",");
    			}
    			sb.append(k + "");
    		}
    		
    	}
    	
    	return sb.toString();
    }
    
    public boolean getRenderDialog() {
    	
    	if(this.selectedItem == null) {
    		return false;
    	}
    	
    	Long prId = this.userSession.get(SessionDataKey.SELECTED_PURCHASE_REQUISITION_ID, Long.class);
    	if(prId != null) {
    		PurchaseRequisition pr = prService.read(prId);
    		if(pr != null) {
    			return pr.getStatus() == Status.APPROVED || pr.getStatus() == Status.INPROGRESS;
    		}
    	}
    	
    	return false;
    }
    
    public void setActiveIndex(String indeces) {
    	// no-op.
    }
    
    public String deletePoItem() {
    	
    	Long id = CurrentInstance.getLongParameter("poItemId");
    	
    	LOG.debug("deleting PoItem#: " + id);
    	
    	poItemService.delete(id);
    	
    	return "/po/main";
    }
    
    public String deletePurchaseOrder() {
    	
    	Long id = CurrentInstance.getLongParameter("poId");
    	
    	LOG.debug("deleting PurchaseOrder#: " + id);
    
    	poService.delete(id);
    	
    	// remove any PO id in session.
    	userSession.put(SessionDataKey.SELECTED_PURCHASE_ORDER_ID, null);
    	
    	
    	return "/po/main";
    }
    
    /**
     * Max value indicator for the (view) quantity spinner component. 
     * @return
     */
    public double getMaxQty() {
    	
    	return maxQty;
    }

	/**
	 * @return the requestDataModel
	 */
	public ListBreeDataModel<ItemRequestDT> getRequestDataModel() {
		return requestDataModel;
	}

	/**
	 * @return the selectedItem
	 */
	public ItemRequestDT getSelectedItem() {
		return selectedItem;
	}

	/**
	 * @param selectedItem the selectedItem to set
	 */
	public void setSelectedItem(ItemRequestDT selectedItem) {
		this.selectedItem = selectedItem;
	}

	/**
	 * @return the supplierId
	 */
	public Long getSupplierId() {
		return supplierId;
	}

	/**
	 * @param supplierId the supplierId to set
	 */
	public void setSupplierId(Long supplierId) {
		this.supplierId = supplierId;
	}

	/**
	 * @return the qtyOrder
	 */
	public double getQtyOrder() {
		return qtyOrder;
	}

	/**
	 * @param qtyOrder the qtyOrder to set
	 */
	public void setQtyOrder(double qtyOrder) {
		this.qtyOrder = qtyOrder;
	}

	/**
	 * @return the unitPrice
	 */
	public double getUnitPrice() {
		return unitPrice;
	}

	/**
	 * @param unitPrice the unitPrice to set
	 */
	public void setUnitPrice(double unitPrice) {
		this.unitPrice = unitPrice;
	}

	/**
	 * @return the saveSupplierPrice
	 */
	public boolean isSaveSupplierPrice() {
		return saveSupplierPrice;
	}

	/**
	 * @param saveSupplierPrice the saveSupplierPrice to set
	 */
	public void setSaveSupplierPrice(boolean saveSupplierPrice) {
		this.saveSupplierPrice = saveSupplierPrice;
	}

	/**
	 * @return the userSession
	 */
	public UserSessionData getUserSession() {
		return userSession;
	}

	/**
	 * @param userSession the userSession to set
	 */
	public void setUserSession(UserSessionData userSession) {
		this.userSession = userSession;
	}
	
}
